#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>

#define DEV_NAME "testdev"		//设备名称
#define CLASS_NAME "my_class"		//class名称
#define DEVICE_NAME "my_device"		//设备名称
#define KEEP_SZ 1024			//保存数据的大小

static struct class *dev_class;
static struct device *dev;
static dev_t devno;
static struct cdev chrdev;
static char *keepbuf;

//字符设备read函数
ssize_t chr_read(struct file *fp, char __user *user_data, size_t count, loff_t *off){
	int ret;

	//将数据拷贝到用户态
	ret = copy_to_user(user_data, keepbuf, count);
	if(ret)
		return -EINVAL;

	printk("kernel chrdev send: %s\n", keepbuf);
	return 0;
}

//字符设备write函数
ssize_t chr_write(struct file *fp, const char __user *user_data, size_t count, loff_t *off){
	int ret;

	memset(keepbuf, 0x0, KEEP_SZ);
	//将数据拷贝的内核态
	ret = copy_from_user(keepbuf, user_data, count);
	if(ret)
		return -EINVAL;

	printk("kernel chrdev recv: %s\n", keepbuf);

	return 0;
}

//字符设备操作函数
static struct file_operations f_op = {
	.read = chr_read,
	.write = chr_write,
};

//初始化函数
static int __init test_init(void){
	int ret;

	//分配保存数据的空间
	keepbuf = kzalloc(KEEP_SZ, GFP_KERNEL);
	if(!keepbuf)
		return -ENOMEM;

	//分配字符设备号
	ret = alloc_chrdev_region(&devno, 0, 1, DEV_NAME);
	if(ret < 0){
		return -EINVAL;
	}

	//字符设备初始化
	cdev_init(&chrdev, &f_op);
	ret = cdev_add(&chrdev, devno, 1);
	if(ret < 0){
		unregister_chrdev_region(devno, 1);
		return -EINVAL;
	}

	//printk("major=%d\n", MAJOR(devno));//输出主设备号日志
	//printk("minor=%d\n", MINOR(devno));//输出从设备号日志

	//创建class
	dev_class = class_create(THIS_MODULE, CLASS_NAME);
	if(!dev_class){
		cdev_del(&chrdev);
		unregister_chrdev_region(devno, 1);
		printk("create_class failed\n");
		return -EINVAL;
	}

	//创建设备
	dev = device_create(dev_class, NULL, devno, NULL, DEVICE_NAME);
	if(!dev){
		class_destroy(dev_class);
		cdev_del(&chrdev);
		unregister_chrdev_region(devno, 1);
		return -EINVAL;
	}

	printk("chrdev.ko rmmod success\n");
	return 0;
}

//退出函数
static void __exit test_exit(void){
	//释放字符设备等相关资源
	device_destroy(dev_class, devno);
	class_destroy(dev_class);
	cdev_del(&chrdev);
	unregister_chrdev_region(devno, 1);
	kfree(keepbuf);
	printk("chrdev.ko rmmod success\n");
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("NFS");
